﻿/*

The MIT License

Copyright (c) 2010 Cartesian Analytics, Inc. 

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

using System;

namespace Pipra.Math
{

    /// <summary>
	/// A square matrix with four rows and four columns with an element type of double.
	/// </summary>
    public class MatrixOrder4 :
        IMatrixOrder4<double>,
        IEquatable<MatrixOrder4>
    {

        /// <summary>
        /// Determines if two values are equal.
        /// </summary>
        /// <param name="left">A matrix.</param>
        /// <param name="right">A matrix.</param>
        /// <returns>True when both matrices are equal.</returns>
		public static bool operator ==(MatrixOrder4 left, MatrixOrder4 right){
			return (ReferenceEquals(null,left) ? ReferenceEquals(null,right) : left.Equals(right));
		}

        /// <summary>
        /// Determines if two values are not equal.
        /// </summary>
        /// <param name="left">A matrix.</param>
        /// <param name="right">A matrix.</param>
        /// <returns>True when both matrices are not equal.</returns>
		public static bool operator !=(MatrixOrder4 left, MatrixOrder4 right) {
			return (ReferenceEquals(null, left) ? !ReferenceEquals(null, right) : !left.Equals(right));
		}

        /// <summary>
        /// Multiplies two matrices and returns a result.
        /// </summary>
        /// <param name="left">Left matrix.</param>
        /// <param name="right">Right matrix.</param>
        /// <returns>Returns the product of two matrices.</returns>
		public static MatrixOrder4 operator *(MatrixOrder4 left, MatrixOrder4 right) {
			return left.Multiply(right);
		}

		/// <summary>
		/// The element at row 0 and column 0.
		/// </summary>
		public double E00;
		/// <summary>
		/// The element at row 0 and column 1.
		/// </summary>
		public double E01;
		/// <summary>
		/// The element at row 0 and column 2.
		/// </summary>
		public double E02;
		/// <summary>
		/// The element at row 0 and column 3.
		/// </summary>
		public double E03;
		/// <summary>
		/// The element at row 1 and column 0.
		/// </summary>
		public double E10;
		/// <summary>
		/// The element at row 1 and column 1.
		/// </summary>
		public double E11;
		/// <summary>
		/// The element at row 1 and column 2.
		/// </summary>
		public double E12;
		/// <summary>
		/// The element at row 1 and column 3.
		/// </summary>
		public double E13;
		/// <summary>
		/// The element at row 2 and column 0.
		/// </summary>
		public double E20;
		/// <summary>
		/// The element at row 2 and column 1.
		/// </summary>
		public double E21;
		/// <summary>
		/// The element at row 2 and column 2.
		/// </summary>
		public double E22;
		/// <summary>
		/// The element at row 2 and column 3.
		/// </summary>
		public double E23;
		/// <summary>
		/// The element at row 3 and column 0.
		/// </summary>
		public double E30;
		/// <summary>
		/// The element at row 3 and column 1.
		/// </summary>
		public double E31;
		/// <summary>
		/// The element at row 3 and column 2.
		/// </summary>
		public double E32;
		/// <summary>
		/// The element at row 3 and column 3.
		/// </summary>
		public double E33;

		/// <summary>
		/// Constructs a new identity matrix.
		/// </summary>
		public MatrixOrder4() {
			SetIdentity();
		}

		/// <summary>
		/// Constructs a new matrix with the given element values.
		/// </summary>
		/// <param name="e00">The value for the element at 0,0.</param>
		/// <param name="e01">The value for the element at 0,1.</param>
		/// <param name="e02">The value for the element at 0,2.</param>
		/// <param name="e03">The value for the element at 0,3.</param>
		/// <param name="e10">The value for the element at 1,0.</param>
		/// <param name="e11">The value for the element at 1,1.</param>
		/// <param name="e12">The value for the element at 1,2.</param>
		/// <param name="e13">The value for the element at 1,3.</param>
		/// <param name="e20">The value for the element at 2,0.</param>
		/// <param name="e21">The value for the element at 2,1.</param>
		/// <param name="e22">The value for the element at 2,2.</param>
		/// <param name="e23">The value for the element at 2,3.</param>
		/// <param name="e30">The value for the element at 3,0.</param>
		/// <param name="e31">The value for the element at 3,1.</param>
		/// <param name="e32">The value for the element at 3,2.</param>
		/// <param name="e33">The value for the element at 3,3.</param>
		public MatrixOrder4(
			double e00, double e01, double e02, double e03,
			double e10, double e11, double e12, double e13,
			double e20, double e21, double e22, double e23,
			double e30, double e31, double e32, double e33
		) {
			E00 = e00;
			E01 = e01;
			E02 = e02;
			E03 = e03;
			E10 = e10;
			E11 = e11;
			E12 = e12;
			E13 = e13;
			E20 = e20;
			E21 = e21;
			E22 = e22;
			E23 = e23;
			E30 = e30;
			E31 = e31;
			E32 = e32;
			E33 = e33;
		}

		/// <summary>
		/// Copies the element values from the given matrix.
		/// </summary>
		/// <param name="m">A matrix to copy from.</param>
		public MatrixOrder4(MatrixOrder4 m) {
			E00 = m.E00;
			E01 = m.E01;
			E02 = m.E02;
			E03 = m.E03;
			E10 = m.E10;
			E11 = m.E11;
			E12 = m.E12;
			E13 = m.E13;
			E20 = m.E20;
			E21 = m.E21;
			E22 = m.E22;
			E23 = m.E23;
			E30 = m.E30;
			E31 = m.E31;
			E32 = m.E32;
			E33 = m.E33;
		}

		/// <summary>
		/// Copies the element values from the given matrix.
		/// </summary>
		/// <param name="m">A matrix to copy from.</param>
		public MatrixOrder4(IMatrixOrder4<double> m) {
			E00 = m.E00;
			E01 = m.E01;
			E02 = m.E02;
			E03 = m.E03;
			E10 = m.E10;
			E11 = m.E11;
			E12 = m.E12;
			E13 = m.E13;
			E20 = m.E20;
			E21 = m.E21;
			E22 = m.E22;
			E23 = m.E23;
			E30 = m.E30;
			E31 = m.E31;
			E32 = m.E32;
			E33 = m.E33;
		}

		/// <summary>
		/// The element at row 0 and column 0.
		/// </summary>
		double IMatrixOrder4<double>.E00 {
			get { return E00; }
			set { E00 = value; }
		}

		/// <summary>
		/// The element at row 0 and column 1.
		/// </summary>
		double IMatrixOrder4<double>.E01 {
			get { return E01; }
			set { E01 = value; }
		}

		/// <summary>
		/// The element at row 0 and column 2.
		/// </summary>
		double IMatrixOrder4<double>.E02 {
			get { return E02; }
			set { E02 = value; }
		}

		/// <summary>
		/// The element at row 0 and column 3.
		/// </summary>
		double IMatrixOrder4<double>.E03 {
			get { return E03; }
			set { E03 = value; }
		}

		/// <summary>
		/// The element at row 1 and column 0.
		/// </summary>
		double IMatrixOrder4<double>.E10 {
			get { return E10; }
			set { E10 = value; }
		}

		/// <summary>
		/// The element at row 1 and column 1.
		/// </summary>
		double IMatrixOrder4<double>.E11 {
			get { return E11; }
			set { E11 = value; }
		}

		/// <summary>
		/// The element at row 1 and column 2.
		/// </summary>
		double IMatrixOrder4<double>.E12 {
			get { return E12; }
			set { E12 = value; }
		}

		/// <summary>
		/// The element at row 1 and column 3.
		/// </summary>
		double IMatrixOrder4<double>.E13 {
			get { return E13; }
			set { E13 = value; }
		}

		/// <summary>
		/// The element at row 2 and column 0.
		/// </summary>
		double IMatrixOrder4<double>.E20 {
			get { return E20; }
			set { E20 = value; }
		}

		/// <summary>
		/// The element at row 2 and column 1.
		/// </summary>
		double IMatrixOrder4<double>.E21 {
			get { return E21; }
			set { E21 = value; }
		}

		/// <summary>
		/// The element at row 2 and column 2.
		/// </summary>
		double IMatrixOrder4<double>.E22 {
			get { return E22; }
			set { E22 = value; }
		}

		/// <summary>
		/// The element at row 2 and column 3.
		/// </summary>
		double IMatrixOrder4<double>.E23 {
			get { return E23; }
			set { E23 = value; }
		}

		/// <summary>
		/// The element at row 3 and column 0.
		/// </summary>
		double IMatrixOrder4<double>.E30 {
			get { return E30; }
			set { E30 = value; }
		}

		/// <summary>
		/// The element at row 3 and column 1.
		/// </summary>
		double IMatrixOrder4<double>.E31 {
			get { return E31; }
			set { E31 = value; }
		}

		/// <summary>
		/// The element at row 3 and column 2.
		/// </summary>
		double IMatrixOrder4<double>.E32 {
			get { return E32; }
			set { E32 = value; }
		}

		/// <summary>
		/// The element at row 3 and column 3.
		/// </summary>
		double IMatrixOrder4<double>.E33 {
			get { return E33; }
			set { E33 = value; }
		}

		/// <summary>
		/// Sets all elements in the matrix.
		/// </summary>
		/// <param name="e00">The value for the element at 0,0.</param>
		/// <param name="e01">The value for the element at 0,1.</param>
		/// <param name="e02">The value for the element at 0,2.</param>
		/// <param name="e03">The value for the element at 0,3.</param>
		/// <param name="e10">The value for the element at 1,0.</param>
		/// <param name="e11">The value for the element at 1,1.</param>
		/// <param name="e12">The value for the element at 1,2.</param>
		/// <param name="e13">The value for the element at 1,3.</param>
		/// <param name="e20">The value for the element at 2,0.</param>
		/// <param name="e21">The value for the element at 2,1.</param>
		/// <param name="e22">The value for the element at 2,2.</param>
		/// <param name="e23">The value for the element at 2,3.</param>
		/// <param name="e30">The value for the element at 3,0.</param>
		/// <param name="e31">The value for the element at 3,1.</param>
		/// <param name="e32">The value for the element at 3,2.</param>
		/// <param name="e33">The value for the element at 3,3.</param>
		public void SetElements(
			double e00, double e01, double e02, double e03,
			double e10, double e11, double e12, double e13,
			double e20, double e21, double e22, double e23,
			double e30, double e31, double e32, double e33
		) {
			E00 = e00;
			E01 = e01;
			E02 = e02;
			E03 = e03;
			E10 = e10;
			E11 = e11;
			E12 = e12;
			E13 = e13;
			E20 = e20;
			E21 = e21;
			E22 = e22;
			E23 = e23;
			E30 = e30;
			E31 = e31;
			E32 = e32;
			E33 = e33;
		}

        /// <summary>
        /// Sets the elements of this matrix to that of the identity matrix.
        /// </summary>
        public void SetIdentity() {
			E00 = E11 = E22 = E33 = 1.0;
			E01 = E02 = E03 = 0;
			E10 = E12 = E13 = 0;
			E20 = E21 = E23 = 0;
			E30 = E31 = E32 = 0;
		}

        private double SubDet(int r0, int r1, int r2, int c0, int c1, int c2)
        {
            return (
                (
                    (Get(r0, c0) * Get(r1, c1) * Get(r2, c2))
                    +
                    (Get(r0, c1) * Get(r1, c2) * Get(r2, c0))
                    +
                    (Get(r0, c2) * Get(r1, c0) * Get(r2, c1))
                )
                -
                (
                    (Get(r2, c0) * Get(r1, c1) * Get(r0, c2))
                    +
                    (Get(r2, c1) * Get(r1, c2) * Get(r0, c0))
                    +
                    (Get(r2, c2) * Get(r1, c0) * Get(r0, c1))
                )
            );
        }

        private double SubDetShort(int ir, int ic)
        {
            return SubDet(
                (ir == 0) ? (1) : (0),
                (ir < 2) ? (2) : (1),
                (ir < 3) ? (3) : (2),
                (ic == 0) ? (1) : (0),
                (ic < 2) ? (2) : (1),
                (ic < 3) ? (3) : (2)
            );
        }

        /// <summary>
        /// Calculates the determinant of the matrix.
        /// </summary>
        /// <returns>The determinant.</returns>
        public double GetDeterminant() {
			double v = (
				(0 != E31)
				? (E31 * SubDetShort(3, 1))
				: 0
			);
			if (0 != E30) {
				v = (v - (E30 * SubDetShort(3, 0)));
			}
			if (0 != E32) {
				v = (v - (E32 * SubDetShort(3, 2)));
			}
			return (
				(0 != E33)
				? (v + (E33 * SubDetShort(3, 3)))
				: v
			);
		}

        /// <summary>
        /// Inverts this matrix.
        /// </summary>
        public void Invert() {
			double det = GetDeterminant();
			if (0 == det || Double.IsNaN(det)) {
				throw new InvalidOperationException();
			}
			double negDet = -det;

            SetElements(
                (SubDetShort(0, 0) / det),
                (SubDetShort(1, 0) / negDet),
                (SubDetShort(2, 0) / det),
                (SubDetShort(3, 0) / negDet),
                
                (SubDetShort(0, 1) / negDet),
                (SubDetShort(1, 1) / det),
                (SubDetShort(2, 1) / negDet),
                (SubDetShort(3, 1) / det),
                
                (SubDetShort(0, 2) / det),
                (SubDetShort(1, 2) / negDet),
                (SubDetShort(2, 2) / det),
                (SubDetShort(3, 2) / negDet),

                (SubDetShort(0, 3) / negDet),
                (SubDetShort(1, 3) / det),
                (SubDetShort(2, 3) / negDet),
                (SubDetShort(3, 3) / det)
            );
		}

        /// <summary>
        /// Generates a matrix which is the inverse.
        /// </summary>
        /// <returns>The inverse of the matrix.</returns>
		public MatrixOrder4 GetInverse() {
			double det = GetDeterminant();
			if (0 == det || Double.IsNaN(det)) {
				throw new InvalidOperationException();
			}
			
			double negDet = -det;
			return new MatrixOrder4(
				(SubDetShort(0, 0) / det),
				(SubDetShort(1, 0) / negDet),
				(SubDetShort(2, 0) / det),
				(SubDetShort(3, 0) / negDet),

				(SubDetShort(0, 1) / negDet),
				(SubDetShort(1, 1) / det),
				(SubDetShort(2, 1) / negDet),
				(SubDetShort(3, 1) / det),

				(SubDetShort(0, 2) / det),
				(SubDetShort(1, 2) / negDet),
				(SubDetShort(2, 2) / det),
				(SubDetShort(3, 2) / negDet),

				(SubDetShort(0, 3) / negDet),
				(SubDetShort(1, 3) / det),
				(SubDetShort(2, 3) / negDet),
				(SubDetShort(3, 3) / det)
			);
		}

        /// <summary>
        /// Generates a matrix which is the inverse.
        /// </summary>
        /// <returns>The inverse of the matrix.</returns>
        IMatrixSquare<double> IMatrixSquare<double>.GetInverse() {
			return GetInverse();
		}

		/// <summary>
		/// Transposes this matrix.
		/// </summary>
		public void Transpose() {
			double tmp = E01;
			E01 = E10;
			E10 = tmp;
			tmp = E02;
			E02 = E20;
			E20 = tmp;
			tmp = E03;
			E03 = E30;
			E30 = tmp;
			tmp = E12;
			E12 = E21;
			E21 = tmp;
			tmp = E13;
			E13 = E31;
			E31 = tmp;
			tmp = E23;
			E23 = E32;
			E32 = tmp;
		}
		/// <summary>
		/// Generates a new matrix which is the transpose of this matrix.
		/// </summary>
		/// <returns>The transpose of this matrix.</returns>
		public MatrixOrder4 GetTransposed() {
			return new MatrixOrder4(
			   E00, E10, E20, E30,
			   E01, E11, E21, E31,
			   E02, E12, E22, E32,
			   E03, E13, E23, E33
		   );
		}
		/// <summary>
		/// Generates a new matrix which is the transpose of this matrix.
		/// </summary>
		/// <returns>The transpose of this matrix.</returns>
		IMatrixSquare<double> IMatrixSquare<double>.GetTransposed() {
			return GetTransposed();
		}

		/// <summary>
		/// The number of rows in this matrix.
		/// </summary>
		int IMatrix<double>.RowCount {
			get { return 4; }
		}
		
        /// <summary>
		/// The number of columns in this matrix.
		/// </summary>
		int IMatrix<double>.ColumnCount {
			get { return 4; }
		}

        /// <summary>
        /// Gets the order of the square matrix.
        /// </summary>
        public int Order{ get { return 4; }}

		/// <summary>
		/// The total number of elements in this matrix.
		/// </summary>
		public int ElementCount {
			get { return 16; }
		}

		/// <summary>
		/// Retreives the element at the given row and column location.
		/// </summary>
		/// <param name="r">The row.</param>
		/// <param name="c">The column.</param>
		/// <returns>The element at the given location.</returns>
		public double Get(int r, int c) {
			if (r < 0 || r > 3) {
				throw new ArgumentOutOfRangeException("r");
			}
			if (c < 0 || c > 3) {
				throw new ArgumentOutOfRangeException("c");
			}
			if (0 == r) {
				return (
					c < 2
					? (c == 0 ? E00 : E01)
					: (c == 2 ? E02 : E03)
				);
			}
			if (1 == r) {
				return (
					c < 2
					? (c == 0 ? E10 : E11)
					: (c == 2 ? E12 : E13)
				);
			}
			if (2 == r) {
				return (
					c < 2
					? (c == 0 ? E20 : E21)
					: (c == 2 ? E22 : E23)
				);
			}
			return (
				c < 2
				? (c == 0 ? E30 : E31)
				: (c == 2 ? E32 : E33)
			);
		}

		/// <summary>
		/// Sets the element at the given row and column to the given <paramref name="value"/>.
		/// </summary>
		/// <param name="r">The row.</param>
		/// <param name="c">The column.</param>
		/// <param name="value">The value to store in the matrix element.</param>
		public void Set(int r, int c, double value) {
			if (r < 0 || r > 3) {
				throw new ArgumentOutOfRangeException("r");
			}
			if (c < 0 || c > 3) {
				throw new ArgumentOutOfRangeException("c");
			}
			if (0 == r) {
				if (0 == c) {
					E00 = value;
				}
				else if (1 == c) {
					E01 = value;
				}
				else if (2 == c) {
					E02 = value;
				}
				else {
					E03 = value;
				}
			}
			else if (1 == r) {
				if (0 == c) {
					E10 = value;
				}
				else if (1 == c) {
					E11 = value;
				}
				else if (2 == c) {
					E12 = value;
				}
				else {
					E13 = value;
				}
			}
			else if (2 == r) {
				if (0 == c) {
					E20 = value;
				}
				else if (1 == c) {
					E21 = value;
				}
				else if (2 == c) {
					E22 = value;
				}
				else {
					E23 = value;
				}
			}
			else {
				if (0 == c) {
					E30 = value;
				}
				else if (1 == c) {
					E31 = value;
				}
				else if (2 == c) {
					E32 = value;
				}
				else {
					E33 = value;
				}
			}
		}

		/// <summary>
		/// Returns a hash code for this instance.
		/// </summary>
		/// <returns>
		/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
		/// </returns>
		public override int GetHashCode() {
			return E00.GetHashCode();
		}

		/// <summary>
		/// Returns a <see cref="System.String"/> that represents this instance.
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/> that represents this instance.
		/// </returns>
		public override string ToString() {
			return String.Concat(
				E00, ',', E01, ',', E02, ',', E03, ",\n",
				E10, ',', E11, ',', E12, ',', E13, ",\n",
				E20, ',', E21, ',', E22, ',', E23, ",\n",
				E30, ',', E31, ',', E32, ',', E33
			);
		}

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
		public override bool Equals(object obj) {
			return null != obj
                && (
                    (obj is MatrixOrder4 && Equals(obj as MatrixOrder4))
                )
            ;
		}

        /// <summary>
        /// Indicates whether another matrix is equal to this instance.
        /// </summary>
        /// <param name="other">A matrix to compare.</param>
        /// <returns><see langword="true"/> when the given matrix is equal to this instance.</returns>
		public bool Equals(MatrixOrder4 other) {
			return !ReferenceEquals(null,other)
				&& E00 == other.E00
				&& E01 == other.E01
				&& E02 == other.E02
				&& E03 == other.E03
				&& E10 == other.E10
				&& E11 == other.E11
				&& E12 == other.E12
				&& E13 == other.E13
				&& E20 == other.E20
				&& E21 == other.E21
				&& E22 == other.E22
				&& E23 == other.E23
				&& E30 == other.E30
				&& E31 == other.E31
				&& E32 == other.E32
				&& E33 == other.E33
			;
		}

        /// <summary>
        /// Multiplies this left matrix by the given <paramref name="right"/> matrix and returns the product.
        /// </summary>
        /// <param name="right">The right matrix to multiply by.</param>
        /// <returns>A product of this matrix multiplied by the given <paramref name="right"/> matrix.</returns>
		public MatrixOrder4 Multiply(MatrixOrder4 right) {
			return new MatrixOrder4(
				((E00 * right.E00) + (E01 * right.E10) + (E02 * right.E20) + (E03 * right.E30)),
				((E00 * right.E01) + (E01 * right.E11) + (E02 * right.E21) + (E03 * right.E31)),
				((E00 * right.E02) + (E01 * right.E12) + (E02 * right.E22) + (E03 * right.E32)),
				((E00 * right.E03) + (E01 * right.E13) + (E02 * right.E23) + (E03 * right.E33)),
				((E10 * right.E00) + (E11 * right.E10) + (E12 * right.E20) + (E13 * right.E30)),
				((E10 * right.E01) + (E11 * right.E11) + (E12 * right.E21) + (E13 * right.E31)),
				((E10 * right.E02) + (E11 * right.E12) + (E12 * right.E22) + (E13 * right.E32)),
				((E10 * right.E03) + (E11 * right.E13) + (E12 * right.E23) + (E13 * right.E33)),
				((E20 * right.E00) + (E21 * right.E10) + (E22 * right.E20) + (E23 * right.E30)),
				((E20 * right.E01) + (E21 * right.E11) + (E22 * right.E21) + (E23 * right.E31)),
				((E20 * right.E02) + (E21 * right.E12) + (E22 * right.E22) + (E23 * right.E32)),
				((E20 * right.E03) + (E21 * right.E13) + (E22 * right.E23) + (E23 * right.E33)),
				((E30 * right.E00) + (E31 * right.E10) + (E32 * right.E20) + (E33 * right.E30)),
				((E30 * right.E01) + (E31 * right.E11) + (E32 * right.E21) + (E33 * right.E31)),
				((E30 * right.E02) + (E31 * right.E12) + (E32 * right.E22) + (E33 * right.E32)),
				((E30 * right.E03) + (E31 * right.E13) + (E32 * right.E23) + (E33 * right.E33))
			);
		}

        /// <summary>
        /// Multiplies this left matrix by the given <paramref name="right"/> matrix and overwrites this matrix with the product.
        /// </summary>
        /// <param name="right">The right matrix to multiply by.</param>
		public void MultiplyAssignment(MatrixOrder4 right) {
			SetElements(
				((E00 * right.E00) + (E01 * right.E10) + (E02 * right.E20) + (E03 * right.E30)),
				((E00 * right.E01) + (E01 * right.E11) + (E02 * right.E21) + (E03 * right.E31)),
				((E00 * right.E02) + (E01 * right.E12) + (E02 * right.E22) + (E03 * right.E32)),
				((E00 * right.E03) + (E01 * right.E13) + (E02 * right.E23) + (E03 * right.E33)),
				((E10 * right.E00) + (E11 * right.E10) + (E12 * right.E20) + (E13 * right.E30)),
				((E10 * right.E01) + (E11 * right.E11) + (E12 * right.E21) + (E13 * right.E31)),
				((E10 * right.E02) + (E11 * right.E12) + (E12 * right.E22) + (E13 * right.E32)),
				((E10 * right.E03) + (E11 * right.E13) + (E12 * right.E23) + (E13 * right.E33)),
				((E20 * right.E00) + (E21 * right.E10) + (E22 * right.E20) + (E23 * right.E30)),
				((E20 * right.E01) + (E21 * right.E11) + (E22 * right.E21) + (E23 * right.E31)),
				((E20 * right.E02) + (E21 * right.E12) + (E22 * right.E22) + (E23 * right.E32)),
				((E20 * right.E03) + (E21 * right.E13) + (E22 * right.E23) + (E23 * right.E33)),
				((E30 * right.E00) + (E31 * right.E10) + (E32 * right.E20) + (E33 * right.E30)),
				((E30 * right.E01) + (E31 * right.E11) + (E32 * right.E21) + (E33 * right.E31)),
				((E30 * right.E02) + (E31 * right.E12) + (E32 * right.E22) + (E33 * right.E32)),
				((E30 * right.E03) + (E31 * right.E13) + (E32 * right.E23) + (E33 * right.E33))
			);
		}

        /// <summary>
        /// Multiplies this left matrix by the given <paramref name="right"/> matrix and returns the product.
        /// </summary>
        /// <param name="right">The right matrix to multiply by.</param>
        /// <returns>A product of this matrix multiplied by the given <paramref name="right"/> matrix.</returns>
		public MatrixOrder4 Multiply(IMatrixOrder4<double> right) {
			return new MatrixOrder4(
				((E00 * right.E00) + (E01 * right.E10) + (E02 * right.E20) + (E03 * right.E30)),
				((E00 * right.E01) + (E01 * right.E11) + (E02 * right.E21) + (E03 * right.E31)),
				((E00 * right.E02) + (E01 * right.E12) + (E02 * right.E22) + (E03 * right.E32)),
				((E00 * right.E03) + (E01 * right.E13) + (E02 * right.E23) + (E03 * right.E33)),
				((E10 * right.E00) + (E11 * right.E10) + (E12 * right.E20) + (E13 * right.E30)),
				((E10 * right.E01) + (E11 * right.E11) + (E12 * right.E21) + (E13 * right.E31)),
				((E10 * right.E02) + (E11 * right.E12) + (E12 * right.E22) + (E13 * right.E32)),
				((E10 * right.E03) + (E11 * right.E13) + (E12 * right.E23) + (E13 * right.E33)),
				((E20 * right.E00) + (E21 * right.E10) + (E22 * right.E20) + (E23 * right.E30)),
				((E20 * right.E01) + (E21 * right.E11) + (E22 * right.E21) + (E23 * right.E31)),
				((E20 * right.E02) + (E21 * right.E12) + (E22 * right.E22) + (E23 * right.E32)),
				((E20 * right.E03) + (E21 * right.E13) + (E22 * right.E23) + (E23 * right.E33)),
				((E30 * right.E00) + (E31 * right.E10) + (E32 * right.E20) + (E33 * right.E30)),
				((E30 * right.E01) + (E31 * right.E11) + (E32 * right.E21) + (E33 * right.E31)),
				((E30 * right.E02) + (E31 * right.E12) + (E32 * right.E22) + (E33 * right.E32)),
				((E30 * right.E03) + (E31 * right.E13) + (E32 * right.E23) + (E33 * right.E33))
			);
		}

        /// <summary>
        /// Multiplies this left matrix by the given <paramref name="right"/> matrix and returns the product.
        /// </summary>
        /// <param name="right">The right matrix to multiply by.</param>
        /// <returns>A product of this matrix multiplied by the given <paramref name="right"/> matrix.</returns>
        IMatrixOrder4<double> IMatrixOrder4<double>.Multiply(IMatrixOrder4<double> right) {
			return Multiply(right);
		}

        /// <summary>
        /// Multiplies this left matrix by the given <paramref name="right"/> matrix and overwrites this matrix with the product.
        /// </summary>
        /// <param name="right">The right matrix to multiply by.</param>
        public void MultiplyAssignment(IMatrixOrder4<double> right) {
			SetElements(
				((E00 * right.E00) + (E01 * right.E10) + (E02 * right.E20) + (E03 * right.E30)),
				((E00 * right.E01) + (E01 * right.E11) + (E02 * right.E21) + (E03 * right.E31)),
				((E00 * right.E02) + (E01 * right.E12) + (E02 * right.E22) + (E03 * right.E32)),
				((E00 * right.E03) + (E01 * right.E13) + (E02 * right.E23) + (E03 * right.E33)),
				((E10 * right.E00) + (E11 * right.E10) + (E12 * right.E20) + (E13 * right.E30)),
				((E10 * right.E01) + (E11 * right.E11) + (E12 * right.E21) + (E13 * right.E31)),
				((E10 * right.E02) + (E11 * right.E12) + (E12 * right.E22) + (E13 * right.E32)),
				((E10 * right.E03) + (E11 * right.E13) + (E12 * right.E23) + (E13 * right.E33)),
				((E20 * right.E00) + (E21 * right.E10) + (E22 * right.E20) + (E23 * right.E30)),
				((E20 * right.E01) + (E21 * right.E11) + (E22 * right.E21) + (E23 * right.E31)),
				((E20 * right.E02) + (E21 * right.E12) + (E22 * right.E22) + (E23 * right.E32)),
				((E20 * right.E03) + (E21 * right.E13) + (E22 * right.E23) + (E23 * right.E33)),
				((E30 * right.E00) + (E31 * right.E10) + (E32 * right.E20) + (E33 * right.E30)),
				((E30 * right.E01) + (E31 * right.E11) + (E32 * right.E21) + (E33 * right.E31)),
				((E30 * right.E02) + (E31 * right.E12) + (E32 * right.E22) + (E33 * right.E32)),
				((E30 * right.E03) + (E31 * right.E13) + (E32 * right.E23) + (E33 * right.E33))
			);
		}

        /// <summary>
        /// Multiplies this left matrix by the given <paramref name="right"/> matrix and returns the product.
        /// </summary>
        /// <param name="right">The right matrix to multiply by.</param>
        /// <returns>A product of this matrix multiplied by the given <paramref name="right"/> matrix.</returns>
		public IMatrix<double> Multiply(IMatrix<double> right) {
			throw new NotImplementedException();
		}

        /// <summary>
        /// Multiplies this left matrix by the given <paramref name="right"/> matrix and overwrites this matrix with the product.
        /// </summary>
        /// <param name="right">The right matrix to multiply by.</param>
        public void MultiplyAssignment(IMatrix<double> right) {
			throw new NotImplementedException();
		}

    }
}
